<?php

/**
 * MNet Library 
 * Copyright (C) 2006-2008 Catalyst IT Ltd (http://www.catalyst.net.nz)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    mnet
 * @subpackage reference
 * @author     Catalyst IT Ltd
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @copyright  (C) 2006-2008 Catalyst IT Ltd http://catalyst.net.nz
 * @copyright  (C) portions from Moodle, (C) Martin Dougiamas http://dougiamas.com
 *
 */

require MNET_DIR.'/mnet_application.php';

/**
 * This class is a reference implementation of mnet_application,
 * the abstraction layer for integrating MNet with a host system.
 * 
 * This reference implementation stores files in a temporary directory,
 * and creates a SQLite database for storing persistent data. You will
 * need to have the PDO and SQLite PHP extensions enabled to use it.
 *
 * To use it, you should set the document root of your web server to
 * this directory, and call the various scripts to communicate with a remote
 * peer.
 * 
 * You may need to manually alter the SQLite database to add new peers.
 * 
 * Feel free to copy this class and adapt it to your local application.
 *
 */
class mnet_reference_system extends mnet_application {
    private $db;
    private $local_wwwroot;
    private $dir_base;

    /**
     * Initialise this reference implementation
     * 
     * This will create a SQLite database under $dir_base to store
     * config and peers under
     * 
     * @param string $local_wwwroot the local wwwroot of this reference implementation (eg. http://test1.example.com)
     * @param string $dir_base the directory base to use
     */
    public function __construct($local_wwwroot, $dir_base) {
        $this->local_wwwroot = $local_wwwroot;
        $this->dir_base = $dir_base;

        if (!file_exists($dir_base)) {
            mkdir($dir_base, 0777, true);
        }

        $db_path = $dir_base.'/mnet_reference.sq3';
        $this->db = new PDO('sqlite:'.$db_path, null, null, array(
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ));

        // test the db
        try {
            $this->db->query('SELECT * FROM peer LIMIT 1');
        }
        catch (PDOException $e) {
            // Assume the peer table is missing and we need to init
            $this->init_db();
        }
    }
    
    /**
	 * Initialise an empty DB with the required schema
     */
    private function init_db() {
		$this->log(MNET_LOG_NOTICE, 'db init');

        $this->db->exec("
        CREATE TABLE peer (
        	wwwroot             VARCHAR NOT NULL PRIMARY KEY,
        	public_key          VARCHAR NULL,
        	server_path			VARCHAR NULL,
        	protocol			VARCHAR NOT NULL DEFAULT '1.0'
        )");

        $this->db->exec("
        CREATE TABLE keypair_history (
        	seq					INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        	certificate			VARCHAR NOT NULL,
        	keypair_pem			VARCHAR NOT NULL
        )");
    }

    /**
     * Return our discovered wwwroot
     *
     * @return string the discovered wwwroot of this reference instance
     */
    public function get_local_wwwroot() {
        return $this->local_wwwroot;
    }

    /**
     * Logs a message to stderr
     */
    public function log($type, $message) {
        file_put_contents($this->dir_base.'/log', date('c').': '.$type.': '.$message.PHP_EOL, FILE_APPEND);
    }
    
    public function get_current_keypair() {
        $keypair = $this->db->query("SELECT certificate,keypair_pem AS keypair_PEM FROM keypair_history ORDER BY seq DESC LIMIT 1")->fetch(PDO::FETCH_ASSOC);
        if (!$keypair) {
            return null;
        }

        return $keypair;
    }

    public function get_keypair_history() {
        return $this->db->query("SELECT certificate,keypair_pem AS keypair_PEM FROM keypair_history ORDER BY seq DESC LIMIT -1 OFFSET 1")->fetchAll(PDO::FETCH_ASSOC);
    }

    public function set_new_keypair($certificate, $keypair_pem) {
        $sth = $this->db->prepare("INSERT INTO keypair_history (certificate, keypair_pem) VALUES(?,?)");
        $sth->execute(array($certificate, $keypair_pem));
    }

    public function get_peer_by_wwwroot($wwwroot) {
        $sth = $this->db->prepare("SELECT * FROM peer WHERE wwwroot = ?");
        $sth->execute(array($wwwroot));
        $data = $sth->fetch(PDO::FETCH_ASSOC);

        if (!$data) {
            return null;
        }

        return mnet_peer::populate($data['wwwroot'], $data['public_key'], $data['server_path'], $data['protocol']);
    }

    public function get_public_key_url() {
        return $this->local_wwwroot.'/publickey.php';
    }

    public function save_peer(mnet_peer &$peer) {
		$peer_arr = array();

        $sth = $this->db->prepare("REPLACE INTO peer (wwwroot, public_key, server_path, protocol) VALUES(?,?,?,?)");
        $sth->execute(array($peer->get_wwwroot(), $peer->get_public_key(), $peer->get_server_path(),$peer->get_protocol()));
    }

	public function peer_is_trusted(mnet_peer $peer) {
		return true;
	}
	
	public function get_server_path() {
	    return '/server.php';
	}
	
	/**
	 * Returns a list of peers known to this reference implementation
	 * 
	 * Note this is not a standard mnet_application API method; it is
	 * provided for convenience.
	 *
	 * @return array an array of mnet_peer records
	 */
	public function get_all_peers() {
		$sth = $this->db->query("SELECT wwwroot FROM peer ORDER BY wwwroot");
		$wwwroots = $sth->fetchAll(PDO::FETCH_COLUMN);
		$peers = array();
		foreach ($wwwroots as $wwwroot) {
			$peers[] = $this->get_peer_by_wwwroot($wwwroot);
		}
		
		return $peers;
	}
}